package com.ideal.manage.esms.service.monitor;

import com.alibaba.fastjson.JSONObject;
import com.ideal.manage.esms.bean.alarm.Alarm;
import com.ideal.manage.esms.bean.alarm.QAlarm;
import com.ideal.manage.esms.bean.diagnostic.DiagnoseResultInfo;
import com.ideal.manage.esms.bean.diagnostic.QDiagnoseResultInfo;
import com.ideal.manage.esms.bean.esms.EsmsIntegration;
import com.ideal.manage.esms.bean.esms.QEsmsIntegration;
import com.ideal.manage.esms.bean.project.*;
import com.ideal.manage.esms.jpa.JpaRepositoryImpl;
import com.ideal.manage.esms.service.ElasticsearchService;
import com.ideal.manage.esms.service.IntegrationService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.joda.time.format.DateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
@Transactional
public class MonitorBlockService {
    private Logger LOG = LoggerFactory.getLogger(MonitorBlockService.class);

    @Autowired
    private EntityManager entityManager;
    @Autowired
    private ElasticsearchService elasticsearchService;

    private JpaRepositoryImpl<ParameterInfo, String> parameterJpaRepository;
    private JpaRepositoryImpl<ProjectManage, String> projectManageRepository;
    private JpaRepositoryImpl<EquipmentManage, String> equipmentManageRepository;
    private JpaRepositoryImpl<DiagnoseResultInfo, String> diagnoseResultInfoRepository;
    private JpaRepositoryImpl<Alarm, String> alarmRepository;
    private JpaRepositoryImpl<EsmsIntegration, Long> integrationRepository;

    @PostConstruct
    public void init() {
        this.parameterJpaRepository = new JpaRepositoryImpl<>(ParameterInfo.class, entityManager);
        this.projectManageRepository = new JpaRepositoryImpl<>(ProjectManage.class, entityManager);
        this.alarmRepository = new JpaRepositoryImpl<>(Alarm.class, entityManager);
        this.equipmentManageRepository = new JpaRepositoryImpl<EquipmentManage, String>(EquipmentManage.class, entityManager);
        this.diagnoseResultInfoRepository = new JpaRepositoryImpl<DiagnoseResultInfo, String>(DiagnoseResultInfo.class, entityManager);
        this.integrationRepository = new JpaRepositoryImpl<EsmsIntegration, Long>(EsmsIntegration.class, entityManager);
    }

    public List<ParameterInfo> getParameterInfo(String ip, LocalDateTime zeroHour) {
        QParameterInfo qParameterInfo = QParameterInfo.parameterInfo;
        List<ParameterInfo> infos = parameterJpaRepository.findAll(qParameterInfo.objName.eq(ParameterInfo.OBJECT_NAME_PROJECT)
                .and(qParameterInfo.isShow.eq(1))
                .and(qParameterInfo.isValid.eq(1)));

        //List<JSONObject> list = this.getHourTotal(ip, zeroHour, "BatVoltage", "MaxAllwDschCurr");
        /**
         * 累计充电量	今日充电量（jrcdl）
         * 累计放电量	今日放电量（jrfdl）
         * 安全运行天数
         * 系统效率	今日放电量（jrfdl），今日充电量（jrcdl）
         * 可储蓄电量	可储蓄电量（kcxdl）
         * 总容量	电池包容量（dcbrl）
         * 累计节省电费	今日节省电费（jrjsdf）
         * */
        ParameterInfo xtxl = null;
        ParameterInfo ljcdl = null;
        ParameterInfo ljfdl = null;
        ParameterInfo sdf = null;
        double hdf = 0;
        Tuple<Double, Double> dl = this.caljrdl(ip, zeroHour);
        for (ParameterInfo info : infos) {
            //省电费
            if (info.getEsName().equals("sdf")) {
                sdf = info;
            }
            //耗电费
            if (info.getEsName().equals("hdf")) {
                hdf = calhdf(ip, zeroHour);
                info.setValue(hdf);
            }
            //安全运行天数
            if (info.getEsName().equals("aqyxts")) {
                info.setValue(calaqyxts(ip));
            }
            //系统效率
            if (info.getEsName().equals("xtxl")) {
                xtxl = info;
            }
            //可储蓄电量
            if (info.getEsName().equals("kcxdl")) {
                info.setValue(calkcxdl(ip));
            }
            //累计充电量
            if (info.getEsName().equals("ljcdl")) {
                ljcdl = info;
            }
            //累计放电量
            if (info.getEsName().equals("ljfdl")) {
                ljfdl = info;
            }
            //总容量
            if (info.getEsName().equals("zrl")) {
                info.setValue(caldcbrl(ip));
            }
            //	累计节省电费
            if (info.getEsName().equals("ljjsdf")) {
                info.setValue(calljsdf(ip));
            }
            //今日充电量
            if (info.getEsName().equals("jrcdl")) {
                info.setValue(dl.v1());
            }
            //今日放电量
            if (info.getEsName().equals("jrfdl")) {
                info.setValue(dl.v2());
            }
        }
        if (xtxl != null) {
            if (dl.v2() == 0.0) {
                xtxl.setValue(0.0);
            } else {
                xtxl.setValue(Math.abs(dl.v1() / dl.v2()));
            }
        }
        if (ljcdl != null) {
            ljcdl.setValue(calljcdl(ip) + dl.v1());
        }
        if (ljfdl != null) {
            ljfdl.setValue(calljfdl(ip) + dl.v2());
        }
        if (sdf != null) {
            sdf.setValue(this.calsdf(ip, zeroHour));
        }
        return infos;
    }

    private double calhdf(String ip, LocalDateTime startHour) {
        Date startTime = Date.from(startHour.toInstant(ZoneOffset.of("+8")));
        Date endTime = Date.from(startHour.plusHours(23).withMinute(59).withSecond(59).toInstant(ZoneOffset.of("+8")));
        QEsmsIntegration qIntegration = QEsmsIntegration.esmsIntegration;

        Double sum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.calTime.between(startTime, endTime).and(qIntegration.type.eq("hdf")))
                .fetchOne();

        Date now = new Date();
        Date calTime = integrationRepository.query()
                .select(qIntegration.calTime)
                .from(qIntegration)
                .limit(1)
                .orderBy(qIntegration.calTime.desc())
                .where(qIntegration.projectIp.eq(ip))
                .fetchOne();
        if (calTime == null) { //容错性，万一此项目ES中没有值
            calTime = new Date();
        }
        //double nextSum = integrationService.calHdf(ip, calTime.getTime() + 1000, now.getTime());
        return Utils.toDouble(sum);
    }

    private double calsdf(String ip, LocalDateTime startHour) {
        Date startTime = Date.from(startHour.toInstant(ZoneOffset.of("+8")));
        Date endTime = Date.from(startHour.plusHours(23).withMinute(59).withSecond(59).toInstant(ZoneOffset.of("+8")));
        QEsmsIntegration qIntegration = QEsmsIntegration.esmsIntegration;

        Double sdfSum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.calTime.between(startTime, endTime).and(qIntegration.type.eq("sdf")))
                .fetchOne();

        Date now = new Date();
        Date calTime = integrationRepository.query()
                .select(qIntegration.calTime)
                .from(qIntegration)
                .limit(1)
                .orderBy(qIntegration.calTime.desc())
                .where(qIntegration.projectIp.eq(ip))
                .fetchOne();
        if (calTime == null) { //容错性，万一此项目ES中没有值
            calTime = new Date();
        }
        //double nextSum = integrationService.calSdf(ip, calTime.getTime() + 1000, now.getTime());
        return Utils.toDouble(sdfSum);
    }

    private double calljsdf(String ip) {
        QEsmsIntegration qIntegration = QEsmsIntegration.esmsIntegration;
        Double sdfSum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.type.eq("sdf").and(qIntegration.projectIp.eq(ip)))
                .fetchOne();
        return Utils.halfUp(sdfSum);
    }

    private double caldcbrl(String ip) {
        QDiagnoseResultInfo qDiagnoseResultInfo = QDiagnoseResultInfo.diagnoseResultInfo;
        String value = diagnoseResultInfoRepository.query()
                .select(qDiagnoseResultInfo.resultVal)
                .from(qDiagnoseResultInfo)
                .where(qDiagnoseResultInfo.equipment.project.ip.eq(ip)
                        .and(qDiagnoseResultInfo.setVal.eq("dcbrl")))
                .fetchOne();
        return NumberUtils.toDouble(value, 0);
    }

    private double calljfdl(String ip) {
        QEsmsIntegration qIntegration = QEsmsIntegration.esmsIntegration;
        Double sum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.projectIp.eq(ip)
                        .and(qIntegration.type.eq("fdl")))
                .fetchOne();
        if (sum == null) {
            return 0;
        }
        return sum;
    }

    protected double calljcdl(String ip) {
        QEsmsIntegration qIntegration = QEsmsIntegration.esmsIntegration;
        Double sum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.projectIp.eq(ip)
                        .and(qIntegration.type.eq("cdl")))
                .fetchOne();
        if (sum == null) {
            return 0;
        }
        return sum;
    }

    protected List<String> getEquipmentManageIds(String ip) {
        ProjectManage project = projectManageRepository.findOne(QProjectManage.projectManage.ip.eq(ip));
        return equipmentManageRepository.query()
                .select(QEquipmentManage.equipmentManage.id)
                .from(QEquipmentManage.equipmentManage)
                .where(QEquipmentManage.equipmentManage.project.id.eq(project.getId()))
                .fetch();
    }

    private double calkcxdl(String ip) {
        List<String> ids = this.getEquipmentManageIds(ip);
        QDiagnoseResultInfo qDiagnoseResultInfo = QDiagnoseResultInfo.diagnoseResultInfo;
        String value = diagnoseResultInfoRepository.query()
                .select(qDiagnoseResultInfo.resultVal)
                .from(qDiagnoseResultInfo)
                .limit(1)
                .where(qDiagnoseResultInfo.equipment.id.in(ids)
                        .and(qDiagnoseResultInfo.setVal.eq("kcxdl")))
                .fetchOne();
        return NumberUtils.toDouble(value, 0);
    }

    private double calaqyxts(String ip) {
        List<String> ids = this.getEquipmentManageIds(ip);

        Date lastDate = alarmRepository.query().select(QAlarm.alarm.createTime)
                .from(QAlarm.alarm)
                .where(QAlarm.alarm.equipmentId.in(ids))
                .orderBy(QAlarm.alarm.createTime.desc())
                .limit(1)
                .fetchOne();
        if (lastDate == null) { //容错性，万一此项目ES中没有值
            lastDate = new Date();
        }
        Period period = Period.between(lastDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
                LocalDate.now());
        return period.getDays();

    }

    //实时监控
    public List<ParameterInfo> getRealTimeInfo(String ip) {
        LocalDateTime zeroHour = LocalDateTime.of(LocalDate.now(),
                LocalTime.of(0, 0, 0));
        return this.getParameterInfo(ip, zeroHour);
    }

    public org.elasticsearch.common.collect.Tuple<Double, Double> caljrdl(String ip, LocalDateTime startHour) {
        Date startTime = Date.from(startHour.toInstant(ZoneOffset.of("+8")));
        Date endTime = Date.from(startHour.plusHours(23).withMinute(59).withSecond(59).toInstant(ZoneOffset.of("+8")));
        QEsmsIntegration qIntegration = QEsmsIntegration.esmsIntegration;

        Double cdlSum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.calTime.between(startTime, endTime).and(qIntegration.type.eq("cdl")))
                .fetchOne();
        Double fdlSum = integrationRepository.query()
                .select(qIntegration.val.sum())
                .from(qIntegration)
                .where(qIntegration.calTime.between(startTime, endTime).and(qIntegration.type.eq("fdl")))
                .fetchOne();
        //不要实时部分，只显示15钟内的数据
        //org.elasticsearch.common.collect.Tuple<Double, Double> dl = integrationService.calDl(ip, calTime.getTime() + 1000, now.getTime());
        return new org.elasticsearch.common.collect.Tuple<>(Utils.toDouble(cdlSum),
                Utils.toDouble(fdlSum));
    }

    public List<JSONObject> getBatteryPack(String projectId) {
        // 1. 找到所有的电池包
        List<EquipmentManage> batteryPackageList = equipmentManageRepository
                .findAll(QEquipmentManage.equipmentManage.project.id.eq(projectId)
                        .and(QEquipmentManage.equipmentManage.equipmentType.eq("2")));
        List<JSONObject> r = new ArrayList<>();


        LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0));
        Date end = Date.from(startTime.plusHours(23).withMinute(59).withSecond(59).toInstant(ZoneOffset.of("+8")));
        Date start = Date.from(startTime.toInstant(ZoneOffset.of("+8")));
        for (EquipmentManage equipmentManage : batteryPackageList) {
            if (StringUtils.isNotEmpty(equipmentManage.getCode())) {
                JSONObject json1 = elasticsearchService.searchNewest(QueryBuilders.boolQuery()
                                .filter(QueryBuilders.existsQuery("BatVoltage"))
                                .filter(QueryBuilders.termQuery("macid", equipmentManage.getCode()))
                                .filter(QueryBuilders.rangeQuery("time").gte(start.getTime()).lte(end.getTime())),
                        key -> key.equals("BatVoltage") || key.equals("BatCurrent") || key.equals("BatSoc"));

                JSONObject voltAndTemp = Utils.nowHour();
                JSONObject queryJson = new JSONObject();
                elasticsearchService.aggregationAvg(QueryBuilders.boolQuery()
                                .filter(QueryBuilders.existsQuery("MaxCellVolt"))
                                .filter(QueryBuilders.termQuery("macid", equipmentManage.getCode()))
                                .filter(QueryBuilders.rangeQuery("time").gte(start.getTime()).lte(end.getTime())),
                        DateHistogramInterval.hours(1),
                        (filedName, time, val) -> {
                            String key = DateTimeFormat.forPattern("yyy-MM-dd HH").print(time);
                            JSONObject json;
                            if (queryJson.containsKey(key)) {
                                json = queryJson.getJSONObject(key);
                            } else {
                                json = new JSONObject();
                                json.put("MaxCellVolt", 0);
                                json.put("MinCellVolt", 0);
                                json.put("MaxTemp", 0);
                                json.put("MinTemp", 0);
                            }
                            json.put(filedName, Utils.convertShowVolt(val));
                            queryJson.put(key, json);
                        },
                        "MaxCellVolt", "MinCellVolt");
                elasticsearchService.aggregationAvg(QueryBuilders.boolQuery()
                                .filter(QueryBuilders.existsQuery("MaxTemp"))
                                .filter(QueryBuilders.termQuery("macid", equipmentManage.getCode()))
                                .filter(QueryBuilders.rangeQuery("time").gte(start.getTime()).lte(end.getTime())),
                        DateHistogramInterval.hours(1),
                        (filedName, time, val) -> {
                            String key = DateTimeFormat.forPattern("yyy-MM-dd HH").print(time);
                            if (queryJson.containsKey(key)) {
                                queryJson.getJSONObject(key).put(filedName, Utils.halfUp(val));
                            }
                        },
                        "MaxTemp", "MinTemp");

                if (json1 != null) {
                    //合并
                    for (String key : voltAndTemp.keySet()) {
                        if (queryJson.containsKey(key)) {
                            voltAndTemp.put(key, queryJson.getJSONObject(key));
                        } else {
                            JSONObject empty = new JSONObject();
                            empty.put("MaxCellVolt", 0);
                            empty.put("MinCellVolt", 0);
                            empty.put("MaxTemp", 0);
                            empty.put("MinTemp", 0);
                            voltAndTemp.put(key, empty);
                        }
                    }
                    json1.put("name", equipmentManage.getName());
                    json1.put("voltAndTemp", voltAndTemp);
                    r.add(json1);
                }
            }
        }

        // 4. 返回
        return r;
    }

    public List<Alarm> findAlarmEquipment(String id) {
        QEquipmentManage qEquipmentManage = QEquipmentManage.equipmentManage;
        List<String> equipmentManageIds = equipmentManageRepository.query()
                .select(qEquipmentManage.id)
                .from(qEquipmentManage)
                .where(qEquipmentManage.project.id.eq(id)
                        .and(qEquipmentManage.isValid.eq(1)
                                .and(qEquipmentManage.equipmentType.eq("2"))))
                .fetch();
        QAlarm qAlarm = QAlarm.alarm;

        //未处理
        return alarmRepository.query()
                .select(qAlarm)
                .from(qAlarm)
                .where(qAlarm.equipmentId.in(equipmentManageIds).and(qAlarm.state.ne(3)))
                .orderBy(qAlarm.createTime.desc())
                .fetch();
    }

}
